package org.peterf.foxish.program;

import java.util.HashMap;
import java.util.Map.Entry;

import org.peterf.foxish.R;
import org.peterf.foxish.util.EngineException;
import org.peterf.foxish.util.Pair;

public class ProgramRepository {

    private static class PPair extends Pair<Integer, Integer> {

        public PPair(Integer first, Integer second) {
            super(first, second);
        }

    }

    private static HashMap<Integer, FragmentShader> fragmentRepo = new HashMap<Integer, FragmentShader>();

    private static HashMap<PPair, Program> programRepo = new HashMap<PPair, Program>();

    private static HashMap<Integer, VertexShader> vertexRepo = new HashMap<Integer, VertexShader>();

    public static void destroy() {
        vertexRepo.clear();
        fragmentRepo.clear();
        programRepo.clear();
    }

    public static Program get(int vertexRes, int fragmentRes) {
        return getProgram(vertexRes, fragmentRes).shared();
    }

    public static void preLoad(int vertexRes, int fragmentRes) {
        getProgram(vertexRes, fragmentRes);
    }

    public static void recreate() {
        for (Entry<Integer, VertexShader> e : vertexRepo.entrySet()) {
            e.getValue().invalidate();
        }
        for (Entry<Integer, FragmentShader> e : fragmentRepo.entrySet()) {
            e.getValue().invalidate();
        }
        for (Entry<PPair, Program> e : programRepo.entrySet()) {
            e.getValue().invalidate();
        }
    }

    private static FragmentShader getFragment(int fragmentRes) {
        FragmentShader f = fragmentRepo.get(fragmentRes);
        if (f == null) {
            f = FragmentShader.compile(fragmentRes);
            if (f == null) {
                throw new EngineException(R.string.foxish_err_fragment_fail);
            }
            fragmentRepo.put(fragmentRes, f);
        } else if (!f.isValid()) {
            f.recompile(fragmentRes);
        }
        return f;
    }

    private static Program getProgram(int vertexRes, int fragmentRes) {
        PPair key = new PPair(vertexRes, fragmentRes);
        Program p = programRepo.get(key);
        if (p == null) {
            VertexShader v = getVertex(vertexRes);
            FragmentShader f = getFragment(fragmentRes);
            p = Program.link(v, f);
            if (p == null) {
                throw new EngineException(R.string.foxish_err_link_fail);
            }
            programRepo.put(key, p);
        } else if (!p.isValid()) {
            p.relink(getVertex(vertexRes), getFragment(fragmentRes));
        }
        return p;
    }

    private static VertexShader getVertex(int vertexRes) {
        VertexShader v = vertexRepo.get(vertexRes);
        if (v == null) {
            v = VertexShader.compile(vertexRes);
            if (v == null) {
                throw new EngineException(R.string.foxish_err_vertex_fail);
            }
            vertexRepo.put(vertexRes, v);
        } else if (!v.isValid()) {
            v.recompile(vertexRes);
        }
        return v;
    }

}
